#include "BrresFile.h"
#include "SwitchEndianness.h"
#include <fstream>
#include <string.h>






using namespace std;

bool readbrres(char *bres , UINT bsize, BrresFile  **r)
{
    BrresNode               *root;
    BrresHeader             *hd;
    BrresSectionHeader      *bsechd;
    UINT                    objcount,brsize;
    USHORT                  headersize,seccount;
    char                    *curptr;
    //if bres buf is not begin with "bres" or "BRES"
    if( *((UINT*)bres) != 0x73657262 && *((UINT*)bres) != 0x53455242)
        return false;

    curptr = bres;
    //if bres buf's length is not correct
    hd = (BrresHeader*)curptr;
    brsize = u32se(hd->filesize);
    if(brsize != bsize)
        return false;
    //get header size and count of section
    headersize = u16se(hd->hlen);
    seccount = u16se(hd->sectioncount);

    //get root node
    curptr += headersize+sizeof(BrresSectionHeader);
    root = GetDirNode(curptr,NULL,headersize+sizeof(BrresSectionHeader));

    if(!root)
        return false;

    *r = new BrresFile;
    (*r)->root = root;
    (*r)->pdata = new UCHAR[bsize];
    memcpy((*r)->pdata,bres,bsize);

    return true;
}


bool readbrresfromfile(const char *filename , BrresFile **r)
{
    UINT    fsize;
    char    *bresbuf;
    ifstream bresfs;
    bool    result;

    //open bres file
    bresfs.open(filename,ios::in | ios::binary);

    //if failed to open brres,return
    if(!bresfs.is_open())
        return false;

    //get file size
    bresfs.seekg(0,ios::end);
    fsize = bresfs.tellg();
    bresfs.seekg(0,ios::beg);

    //allocate memory and read file
    bresbuf = new char[fsize];
    bresfs.read(bresbuf,fsize);
    bresfs.close();
    //read bres
    result = readbrres(bresbuf,fsize,r);

    //free memory
    delete[] bresbuf;
    return result;
}

void freebrres(BrresFile  *r)
{
    if( !r )
        return;

    //free file buffer
    if(r->pdata)
        delete[] r->pdata;

    FreeBrresNode(r->root);
}





BrresNode*      GetDirNode(char *bres,BrresNode *parent,UINT offset)
{
    if(!bres)
        return NULL;

    BrresNode   *r , *child;
    BrresIndex  *bi;
    char        *cur;
    UINT        bioffset,binameoffset,binlen;

    BrresIndexGroupHead *igh = (BrresIndexGroupHead*)bres;
    UINT    igsize = u32se(igh->igsize);
    UINT    ccount = u32se(igh->icount);

    if( igsize != sizeof(BrresIndexGroupHead) + sizeof(BrresIndex)*ccount )
        return NULL;    //if so , it will be not a directory but a sub file


    r = new BrresNode;
    //r->childcount = ccount ;
    r->offset = offset;
    if(parent)          //if this node is a child ,its offset must add parent's offset
    {
        r->parent = parent;
        r->offset += parent->offset;
    }

    cur = bres + sizeof(BrresIndexGroupHead);

    for(int i=0;i<ccount;i++,cur+=sizeof(BrresIndex))
    {
        bi = (BrresIndex*)cur;
        binameoffset = u32se(bi->nameoffset);
        bioffset = u32se(bi->dataoffset);

        child = GetDirNode(bres+bioffset,r,bioffset);
        if(!child)
        {
            child = GetSubFileNode(bres+bioffset,r,bioffset);
            if(!child)
                continue;
        }

        binlen = u32se(*((UINT*)(bres+binameoffset-4)));
        child->nodename = new char[binlen+1];
        strcpy(child->nodename,bres+binameoffset);

        r->children.push_back(child);

    }

    r->childcount = r->children.size();

    return r;

}


BrresNode*      GetSubFileNode(char *bres,BrresNode *parent,UINT offset)
{
    if(!bres)
        return NULL;

    BrresSubFileHeader *sfh = (BrresSubFileHeader*)bres;
    UINT        sflen;

    BrresNode   *r = new BrresNode;
    r->offset = offset;

    if(parent)
    {
        r->offset += parent->offset;
        r->parent = parent;
    }
    r->childcount = 0;

    return r;

}

void  FreeBrresNode(BrresNode *bnode)
{
    if(!bnode)
        return;

    delete[] bnode->nodename;
    //free children node
    for(int i=0;i<bnode->childcount;i++)
        FreeBrresNode(bnode->children[i]);

    bnode->children.clear();

    delete bnode;

}


